package edu.xidian.onlinedocument.service;

import edu.xidian.onlinedocument.entity.DocumentData;
import edu.xidian.onlinedocument.entity.DocumentEditor;
import edu.xidian.onlinedocument.handler.excpetions.custom.CustomException;
import edu.xidian.onlinedocument.repository.DocumentEditorJPARepository;
import edu.xidian.onlinedocument.repository.OperationDataJPARepository;
import edu.xidian.onlinedocument.views.ResultCode;
import edu.xidian.onlinedocument.repository.DocumentJPARepository;
import edu.xidian.onlinedocument.repository.UserDataJPARepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * Document相关业务
 * 主要涉及文档列表相关业务，包括文档的增、删、改、查
 */
@Service
public class DocumentService {
    private final DocumentJPARepository documentJPARepository;

    private final UserDataJPARepository userDataJPARepository;

    private final DocumentEditorJPARepository documentEditorJPARepository;

    private final OperationDataJPARepository operationDataJPARepository;

    private final CustomDraw customDraw;

    @Autowired
    public DocumentService(DocumentJPARepository documentJPARepository,
                           UserDataJPARepository userDataJPARepository,
                           DocumentEditorJPARepository documentEditorJPARepository,
                           OperationDataJPARepository operationDataJPARepository,
                           CustomDraw customDraw) {
        this.documentJPARepository = documentJPARepository;
        this.userDataJPARepository = userDataJPARepository;
        this.documentEditorJPARepository = documentEditorJPARepository;
        this.operationDataJPARepository = operationDataJPARepository;
        this.customDraw = customDraw;
    }

    /**
     * 查询所有文档
     * @return List<DocumentData>
     */
    public List<DocumentData> getAllDocuments() {
        return documentJPARepository.findAll();
    }

    /**
     * 根据用户id查询创建的文档
     * @param userId 用户id
     * @return List<DocumentData>
     */
    public List<DocumentData> getAllDocumentListAndUserDataByUserId(Integer userId) {
        customDraw.checkUserId(userId);
        List<DocumentData> documentData = this.findDocumentDataByUserId(userId);
        return customDraw.takeDocumentInfoAndUserInfo(documentData);
    }

    /**
     * 根据用户num查询创建的文档
     * @param userNum 用户账号
     * @return List<DocumentData>
     */
    public List<DocumentData> getAllDocumentListAndUserDataByUserNum(String userNum) {
        customDraw.checkUserNum(userNum);
        List<DocumentData> documentData = this.findDocumentDataByUserNum(userNum);
        return customDraw.takeDocumentInfoAndUserInfo(documentData);
    }

    /**
     * 根据文档id查询文档信息（不包括创建者信息）
     * @param docId 文档id
     * @return 文档信息（不包括创建者信息）
     */
    public DocumentData getDocumentDataByDocId(Integer docId){
        customDraw.checkDocId(docId);
        DocumentData documentData = this.findDocumentDataByDocId(docId);
        return customDraw.takeDocumentInfo(documentData);
    }

    /**
     * 根据文档id查询文档信息（包括创建者信息）
     * @param docId 文档id
     * @return 文档信息（包括创建者信息）
     */
    public DocumentData getDocumentDataAndCreatorInfoByDocId(Integer docId){
        customDraw.checkDocId(docId);
        DocumentData documentData = this.findDocumentDataByDocId(docId);
        return customDraw.takeDocumentInfoAndUserInfo(documentData);
    }

    /**
     * 根据文档id查询文档信息（包括编辑者信息）
     * @param docId 文档id
     * @return 文档信息（包括编辑者信息）
     */
    public DocumentData getDocumentDataAndEditorInfoByDocId(Integer docId){
        customDraw.checkDocId(docId);
        DocumentData documentData = this.findDocumentDataByDocId(docId);
        return customDraw.takeDocumentInfoAndEditors(documentData);
    }

    /**
     * 根据文档id查询文档信息（包括创建者信息和编辑者信息）
     * @param docId 文档id
     * @return 文档信息（包括创建者信息和编辑者信息）
     */
    public DocumentData getDocumentDataAndCreatorInfoAndEditorInfoByDocId(Integer docId){
        customDraw.checkDocId(docId);
        DocumentData documentData = this.findDocumentDataByDocId(docId);
        return customDraw.takeDocumentInfoAndUserInfoAndEditors(documentData);
    }

    /**
     * 增加文档
     * @param docName 文档名
     * @param userId 用户id
     * @return 插入的行数，失败时返回值<0
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public int saveOneDocument(String docName, Integer userId) {
        customDraw.checkDocName(docName);
        customDraw.checkUserId(userId);
        int docVersion = 1;
        Date createTime = new Date();
        boolean isDelete = false;

        DocumentData documentData = new DocumentData();
        documentData.init(docName, userId, docVersion, createTime, createTime, isDelete);
        documentData = documentJPARepository.saveAndFlush(documentData);
        if(documentData == null) {
            throw new CustomException(ResultCode.SQL_INSERT_ERROR);
        }
        return documentData.getDocId();
    }

    /**
     * 删除文档（软删除），即放入回收站
     * @param docId 文档id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void deleteOneDocumentSoft(Integer docId) {
        customDraw.checkDocId(docId);
        DocumentData docData = this.findDocumentDataByDocId(docId);
        if(this.isDeleteByCreator(docData)){
            throw new CustomException(ResultCode.DOCUMENT_FLAG_IS_DELETE);
        }
        docData.setDocIsDelete(true);
        updateCreatorOperationTime(docData, new Date());
        documentJPARepository.save(docData);
    }

    /**
     * 删除文档（硬删除）
     * @param docId 文档id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void deleteOneDocumentPhysically(int docId) {
        customDraw.checkDocId(docId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        documentEditorJPARepository.deleteAllByDocId(docId);
        documentJPARepository.deleteDocumentDataByDocId(docId);
    }

    /**
     * 恢复一个文档，从回收站中取出
     * @param docId 文档id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void renewOneDocument(Integer docId) {
        customDraw.checkDocId(docId);
        DocumentData documentData = this.findDocumentDataByDocId(docId);
        if(!this.isDeleteByCreator(documentData)){
            throw new CustomException(ResultCode.DOCUMENT_FLAG_IS_NOT_DELETE);
        }
        documentData.setDocIsDelete(false);
        updateCreatorOperationTime(documentData, new Date());
        documentJPARepository.save(documentData);
    }

    /**
     * 修改文档名
     * @param docId 文档id
     * @param docName 文档名
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void alterOneDocument(Integer docId, String docName) {
        customDraw.checkDocId(docId);
        customDraw.checkDocName(docName);
        DocumentData docData = this.findDocumentDataByDocId(docId);
        if(this.isDeleteByCreator(docData)){
            throw new CustomException(ResultCode.DOCUMENT_FLAG_IS_DELETE);
        }
        docData.setDocName(docName);
        updateCreatorOperationTime(docData, new Date());
        documentJPARepository.save(docData);
    }

    /**
     * 更新文档的操作时间以及编辑者操作文档的时间
     * @param docId 文档id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void updateOperationTime(int docId, int userId) {
        documentJPARepository.saveAndFlush(this.updateCreatorOperationTime(docId, new Date()));
        documentEditorJPARepository.saveAndFlush(this.updateEditorOperationTime(docId, userId, new Date()));
    }

    /**
     * 加入文档编辑
     * @param docId 文档id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void addEditorDocument(int docId, int userId) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserIdAndDocEditorIsDeleteIsTrue(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_OUT_EDITOR);
        }
        if(documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_IN_EDITOR);
        }
        DocumentEditor documentEditor = new DocumentEditor();
        documentEditor.setDocId(docId);
        documentEditor.setEditorId(userId);
        documentEditor.setDocEditorEditTime(new Date());
        documentEditor.setDocEditorIsDelete(false);
        documentEditor = documentEditorJPARepository.saveAndFlush(documentEditor);
    }

    /**
     * 退出文档编辑，即将文档放入回收站
     * @param docId 文档id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void deleteEditorDocument(int docId, int userId) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserIdAndDocEditorIsDeleteIsFalse(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_OUT_EDITOR);
        }
        DocumentEditor documentEditor = documentEditorJPARepository.findDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId);
        // 删除
        documentEditor.setDocEditorIsDelete(true);
        // 插入删除时间
        updateEditorDeleteOperationTime(documentEditor, new Date());
        documentEditorJPARepository.save(documentEditor);
    }

    /**
     * 恢复文档，将其从回收站中取出
     * @param docId 文档id
     * @param userId 用户id
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void renewEditorDocument(int docId, int userId) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        if(documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserIdAndDocEditorIsDeleteIsFalse(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_OUT_EDITOR);
        }
        DocumentEditor documentEditor = documentEditorJPARepository.findDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId);
        // 恢复
        documentEditor.setDocEditorIsDelete(false);
        // 删除时间置空
        updateEditorDeleteOperationTime(documentEditor, null);
        documentEditorJPARepository.save(documentEditor);
    }

    public boolean existsByDocId(Integer docId) {
        customDraw.checkDocId(docId);
        return documentJPARepository.existsDocumentDataByDocId(docId);
    }

    public boolean existsByDocIdAndIsNotDelete(Integer docId) {
        customDraw.checkDocId(docId);
        return documentJPARepository.existsDocumentDataByDocIdAndDocIsDeleteIsFalse(docId);
    }

    public boolean existsByCreatorId(Integer userId) {
        customDraw.checkDocId(userId);
        return documentJPARepository.existsDocumentDataByCreatorData_UserId(userId);
    }

    public boolean existsByDocIdAndEditorId(Integer docId, Integer userId) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        return documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId);
    }

    /**
     * 以下为组件方法
     */

    /**
     * 查询文档是否在回收站中
     * @param docId docId
     * @return 在回收站中 true / 不在 false
     */
    private boolean isDeleteByCreator(int docId) {
        customDraw.checkDocId(docId);
        return documentJPARepository.existsDocumentDataByDocIdAndDocIsDeleteIsTrue(docId);
    }

    private boolean isDeleteByCreator(DocumentData documentData) {
        if(documentData == null) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        return documentData.getDocIsDelete();
    }

    private boolean isDeleteByEditor(int docId) {
        customDraw.checkDocId(docId);
        return documentJPARepository.existsDocumentDataByDocIdAndDocIsDeleteIsTrue(docId);
    }

    private boolean isDeleteByEditor(DocumentData documentData) {
        if(documentData == null) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        return documentData.getDocIsDelete();
    }

    private DocumentData findDocumentDataByDocId(int docId){
        customDraw.checkDocId(docId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        return documentJPARepository.findDocumentDataByDocId(docId);
    }

    private List<DocumentData> findDocumentDataByUserNum(String userNum){
        customDraw.checkUserNum(userNum);
        if(!documentJPARepository.existsDocumentDataByCreatorData_UserNum(userNum)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        return documentJPARepository.findAllByCreatorData_UserNum(userNum);
    }

    private List<DocumentData> findDocumentDataByUserId(int userId){
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByCreatorData_UserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        return documentJPARepository.findAllByCreatorData_UserId(userId);
    }

    private List<DocumentData> findDocumentDataByUserIdAndDocIdDeleteFalse(int userId){
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByCreatorData_UserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        return documentJPARepository.findAllByCreatorData_UserIdAndDocIsDeleteIsFalse(userId);
    }

    private List<DocumentData> findDocumentDataByUserIdAndDocIdDeleteTrue(int userId){
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByCreatorData_UserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        return documentJPARepository.findAllByCreatorData_UserIdAndDocIsDeleteIsTrue(userId);
    }

    private boolean isDocumentExist(int docId) {
        customDraw.checkDocId(docId);
        return documentJPARepository.existsById(docId);
    }

    /**
     * 创建者更新操作时间
     * @param docId docId
     * @param lastTime 最新的时间
     * @return 更新完后的文档数据
     */
    private DocumentData updateCreatorOperationTime(int docId, Date lastTime) {
        customDraw.checkDocId(docId);
        customDraw.checkDate(lastTime);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        DocumentData documentData = documentJPARepository.findDocumentDataByDocId(docId);
        documentData.setDocUpdateTime(lastTime);
        return documentData;
    }

    private DocumentData updateCreatorOperationTime(DocumentData documentData, Date lastTime) {
        Integer docId = documentData.getDocId();
        customDraw.checkDocId(documentData.getDocId());
        customDraw.checkDate(lastTime);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        documentData.setDocUpdateTime(lastTime);
        return documentData;
    }

    /**
     * 编辑者更新操作时间
     * @param docId 文档id
     * @param userId 编辑者id
     * @param time 最新操作时间
     * @return 修改完的文档
     */
    private DocumentEditor updateEditorOperationTime(int docId, int userId, Date time) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        customDraw.checkDate(time);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        DocumentEditor documentEditor = documentEditorJPARepository.findDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId);
        documentEditor.setDocEditorDeleteTime(time);
        return documentEditor;
    }

    private DocumentEditor updateEditorOperationTime(DocumentEditor documentEditor, Date time) {
        customDraw.checkDocId(documentEditor.getDocId());
        customDraw.checkUserId(documentEditor.getEditorId());
        customDraw.checkDate(time);
        if(!documentJPARepository.existsDocumentDataByDocId(documentEditor.getDocId())) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(documentEditor.getEditorId())) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(documentEditor.getDocId(), documentEditor.getEditorId())) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        documentEditor.setDocEditorDeleteTime(time);
        return documentEditor;
    }

    /**
     * 编辑者更新删除时间
     * @param docId 文档id
     * @param userId 编辑者id
     * @param lastTime 最新删除时间
     * @return 修改完的文档
     */
    private DocumentEditor updateEditorDeleteOperationTime(int docId, int userId, Date lastTime) {
        customDraw.checkDocId(docId);
        customDraw.checkUserId(userId);
        if(!documentJPARepository.existsDocumentDataByDocId(docId)) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId)) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        DocumentEditor documentEditor = documentEditorJPARepository.findDocumentEditorByDocumentData_DocIdAndEditorData_UserId(docId, userId);
        documentEditor.setDocEditorDeleteTime(lastTime);
        return documentEditor;
    }

    private DocumentEditor updateEditorDeleteOperationTime(DocumentEditor documentEditor, Date time) {
        customDraw.checkDocId(documentEditor.getDocId());
        customDraw.checkUserId(documentEditor.getEditorId());
        if(!documentJPARepository.existsDocumentDataByDocId(documentEditor.getDocId())) {
            throw new CustomException(ResultCode.DOCUMENT_NOT_EXIST);
        }
        if(!userDataJPARepository.existsUserDataByUserId(documentEditor.getEditorId())) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        if(!documentEditorJPARepository.existsDocumentEditorByDocumentData_DocIdAndEditorData_UserId(documentEditor.getDocId(), documentEditor.getEditorId())) {
            throw new CustomException(ResultCode.DOCUMENT_IS_NOT_IN_EDITOR);
        }
        documentEditor.setDocEditorDeleteTime(time);
        return documentEditor;
    }
}